package fr.mywiki.model.queries;

import java.rmi.RemoteException;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;

import fr.mywiki.business.entry.EntryLite;
import fr.mywiki.business.structure.StructureLite;
import fr.mywiki.model.ejbAdapter.DataController;

/**
 * This class deals with the management of the links between structures and
 * entries.
 */
public class EntryStructureLinkController {
	/** Name of the table */
	private static final String TABLE_NAME = "entry_structure";

	/** Name of the field representing the entry id */
	private static final String FIELD_ENTRY = "entryid";

	/** Name of the field representing the structure id */
	private static final String FIELD_STRUCTURE = "structureid";

	/** Name of the field representing the type of link */
	private static final String FIELD_TYPE = "type";

	/**
	 * A link entry-structure having that type means accounts from the structure
	 * can consult the entry.
	 */
	public static final String TYPE_READ = "read";

	/**
	 * A link entry-structure having that type means accounts from the structure
	 * can edit the entry.
	 */
	public static final String TYPE_EDIT = "edit";

	/* Links additions */
  /**
   * Adds read links.
   * 
   * @param entry
   *          the entry
   * @param structures
   *          a Collection of StructureLite objects.
   * @throws SQLException
   */
  public static void addReadLinks(EntryLite entry, Collection structures)
      throws SQLException {
    Iterator itStruct = structures.iterator();
    while (itStruct.hasNext()) {
      StructureLite struct = (StructureLite) itStruct.next();
      addReadLink(entry, struct);
    }
  }
  
  /**
   * Adds edit links.
   * 
   * @param entry
   *          the entry
   * @param structures
   *          a Collection of StructureLite objects.
   * @throws SQLException
   */
  public static void addEditLinks(EntryLite entry, Collection structures)
      throws SQLException {
    Iterator itStruct = structures.iterator();
    while (itStruct.hasNext()) {
      StructureLite struct = (StructureLite) itStruct.next();
      addEditLink(entry, struct);
    }
  }
  
	/**
   * Adds a read type link.
   * 
   * @param entry
   *          the entry.
   * @param structure
   *          the structure.
   */
	public static void addReadLink(EntryLite entry, StructureLite structure)
			throws SQLException {
		addLink(entry, structure, TYPE_READ);
	}

	/**
	 * Adds a edit type link.
	 * 
	 * @param entry
	 *          the entry.
	 * @param structure
	 *          the structure.
	 */
	public static void addEditLink(EntryLite entry, StructureLite structure)
			throws SQLException {
		addLink(entry, structure, TYPE_EDIT);
	}

	/**
	 * Adds a new link of the correct type between the given entry and structure.
	 * If the link already exists, the insertion is ignored/not done.
	 * 
	 * @param entry
	 *          a <code>Entrylite</code> object.
	 * @param structure
	 *          a <code>StructureLite</code> object.
	 * @param type
	 *          a link type
	 */
	private static void addLink(EntryLite entry, StructureLite structure,
			String type) throws SQLException {
		String sql = "INSERT IGNORE INTO " + TABLE_NAME + " VALUES("
				+ entry.getId().toString() + ", " + structure.getId().toString() + ", '"
				+ type + "');";
		DatabaseAdapter.executeUpdateQuery(sql);
	}

	/* Links removals */
	/**
	 * Removes the read link between entry and structure.
	 */
	public static void removeReadLink(EntryLite entry, StructureLite structure)
			throws SQLException {
		removeLink(entry, structure, TYPE_READ);
	}

	/**
	 * Removes the edit link between entry and structure.
	 */
	public static void removeEditLink(EntryLite entry, StructureLite structure)
			throws SQLException {
		removeLink(entry, structure, TYPE_EDIT);
	}

	private static void removeLink(EntryLite entry, StructureLite structure,
			String type) throws SQLException {
		String sql = "DELETE FROM " + TABLE_NAME + " WHERE " + FIELD_ENTRY + "="
				+ entry.getId().toString() + " AND " + FIELD_STRUCTURE + "="
				+ structure.getId().toString() + " AND " + FIELD_TYPE + "='" + type+"';";
		DatabaseAdapter.executeUpdateQuery(sql);
	}
  
  /**
   * Removes all the view links of the entry given in parameter. 
   */
  public static void removeAllViewLinks(EntryLite entry) throws SQLException {
    removeAllLinks(entry,TYPE_READ);
  }
  
  /**
   * Removes all the edit links of the entry given in parameter. 
   */
  public static void removeAllEditLinks(EntryLite entry) throws SQLException {
    removeAllLinks(entry,TYPE_EDIT);
  }
  
  private static void removeAllLinks(EntryLite entry, String type) throws SQLException {
    String sql = "DELETE FROM " + TABLE_NAME + " WHERE " + FIELD_ENTRY + "="
        + entry.getId().toString() + " AND " + FIELD_TYPE + "='" + type+"';";
    DatabaseAdapter.executeUpdateQuery(sql);
  }

	/* Links consultations */
	/**
	 * Returns the list of entries linked to the structure in reading, ie the
	 * entries an account from the structure can read.
	 * 
	 * @param structure
	 *          a <code>StructureLite</code> object.
	 * @return a <code>Collection</code> of <code>EntryLite</code> objects.
	 */
	public static Collection getReadLinks(StructureLite structure)
			throws SQLException, RemoteException {
		return getLinks(structure, TYPE_READ);
	}

	/**
	 * Returns the list of entries linked to the structure in editing, ie the
	 * entries an account from the structure can edit.
	 * 
	 * @param structure
	 *          a <code>StructureLite</code> object.
	 * @return a <code>Collection</code> of <code>EntryLite</code> objects.
	 */
	public static Collection getEditLinks(StructureLite structure)
			throws SQLException, RemoteException {
		return getLinks(structure, TYPE_EDIT);
	}

	/**
	 * Returns the list of structures linked to the structure in reading, ie the
	 * structures whose accounts can read the entry.
	 * 
	 * @param entry
	 *          a <code>EntryLite</code> object.
	 * @return a <code>Collection</code> of <code>StructureLite</code>
	 *         objects.
	 */
	public static Collection getReadLinks(EntryLite entry)
			throws RemoteException, SQLException {
		return getLinks(entry, TYPE_READ);
	}

	/**
	 * Returns the list of structures linked to the structure in editing, ie the
	 * structures whose accounts can edit the entry.
	 * 
	 * @param entry
	 *          a <code>EntryLite</code> object.
	 * @return a <code>Collection</code> of <code>StructureLite</code>
	 *         objects.
	 */
	public static Collection getEditLinks(EntryLite entry)
			throws RemoteException, SQLException {
		return getLinks(entry, TYPE_EDIT);
	}

	private static Collection getLinks(EntryLite entry, String type)
			throws SQLException, RemoteException {
		Collection structures = new ArrayList();

		String sql = "SELECT " + FIELD_STRUCTURE + " FROM " + TABLE_NAME
				+ " WHERE " + FIELD_ENTRY + "=" + entry.getId().toString() + " AND "
				+ FIELD_TYPE + "='" + type + "';";

		Iterator itRes = DatabaseAdapter.executeSelectQuery(sql).iterator();
		while (itRes.hasNext()) {
			Long structureId = (Long) ((Object[]) itRes.next())[0];
			StructureLite structure = (StructureLite) DataController.getValue(
					structureId, StructureLite.TYPE_ENT);
			structures.add(structure);
		}

		return structures;
	}

	private static Collection getLinks(StructureLite structure, String type)
			throws SQLException, RemoteException {
		Collection entries = new ArrayList();

		String sql = "SELECT " + FIELD_ENTRY + " FROM " + TABLE_NAME + " WHERE "
				+ FIELD_STRUCTURE + "=" + structure.getId().toString() + " AND "
				+ FIELD_TYPE + "='" + type + "';";

		Iterator itRes = DatabaseAdapter.executeSelectQuery(sql).iterator();
		while (itRes.hasNext()) {
			Long entryId = (Long) ((Object[]) itRes.next())[0];
			EntryLite entry = (EntryLite) DataController.getValue(entryId,
					EntryLite.TYPE_ENT);
			entries.add(entry);
		}

		return entries;
	}

	/**
	 * Determines if there is a read link between the entry and the structure
	 * given in parameters.
	 * 
	 * @param entry
	 *          a <code>EntryLite</code> object.
	 * @param structure
	 *          a <code>StructureLite</code> object.
	 * @return true iif there is a direct read link between entry and structure.
	 */
	public static boolean hasReadLink(EntryLite entry, StructureLite structure)
			throws SQLException {
		return hasLink(entry, structure, TYPE_READ);
	}

	/**
	 * Determines if there is a edit link between the entry and the structure
	 * given in parameters.
	 * 
	 * @param entry
	 *          a <code>EntryLite</code> object.
	 * @param structure
	 *          a <code>StructureLite</code> object.
	 * @return true iif there is a direct edit link between entry and structure.
	 */
	public static boolean hasEditLink(EntryLite entry, StructureLite structure)
			throws SQLException {
		return hasLink(entry, structure, TYPE_EDIT);
	}

	private static boolean hasLink(EntryLite entry, StructureLite structure,
			String type) throws SQLException {
		String sql = "SELECT * FROM " + TABLE_NAME + " WHERE " + FIELD_ENTRY + "="
				+ entry.getId().toString() + " AND " + FIELD_STRUCTURE + "="
				+ structure.getId().toString() + " AND " + FIELD_TYPE + "='" + type+"';";
		return DatabaseAdapter.executeSelectQuery(sql).getRecordsCount() > 0;
	}
	
	/**
	 * Returns the list of entries connected to no structure in read mode, that is to say the
	 * entries visible by anyone (connected or not).
	 * 
	 * @return a <code>Collection</code> of<code>StructureLite</code> objects.
	 */
	public static Collection getReadUnlinkedEntries() throws SQLException, RemoteException {
		return getUnlinkedEntries(TYPE_READ);
	}
	
	/**
	 * Returns the list of entries connected to no structure in edit mode, that is to say the
	 * entries visible by anyone (connected or not).
	 * 
	 * @return a <code>Collection</code> of<code>StructureLite</code> objects.
	 */
	public static Collection getEditUnlinkedEntries() throws SQLException, RemoteException {
		return getUnlinkedEntries(TYPE_EDIT);
	}
	
	private static Collection getUnlinkedEntries(String mode) throws SQLException, RemoteException {
		Collection entries = new ArrayList();
		
		String sql = "SELECT id FROM " + EntryLite.TYPE_ENT
				+ " WHERE id NOT IN (SELECT " + FIELD_ENTRY + " FROM " + TABLE_NAME
				+ " WHERE "+FIELD_TYPE+"='"+mode+"')";

		Iterator itRes = DatabaseAdapter.executeSelectQuery(sql).iterator();
		while (itRes.hasNext()) {
			Long entryId = (Long) ((Object[]) itRes.next())[0];
			EntryLite entry = (EntryLite) DataController.getValue(entryId,
					EntryLite.TYPE_ENT);
			entries.add(entry);
		}

		return entries;
	}
}
